<?php
namespace ZyucMini\Core;

use ZyucMini\Core\Logger;

/**
 * Error handler class
 * 
 * 错误异常处理类，用于捕获运行时错误和异常，支持自定义触发错误
 * 
 * @package ZyucMini\Core
 */
class Error
{
    /**
     * 默认日志路径
     */
    const DEFAULT_LOG_PATH = '/logs/zyuc_miniv1_error.log';
    
    /**
     * 日志文件路径
     * @var string
     */
    protected $logPath;
    
    /**
     * 是否已处理过关闭错误
     * @var bool
     */
    protected $shutdownHandled = false;
    
    /**
     * 错误级别映射
     * @var array
     */
    protected $errorLevels = [
        E_ERROR             => 'Error',
        E_WARNING           => 'Warning',
        E_PARSE             => 'Parse Error',
        E_NOTICE            => 'Notice',
        E_CORE_ERROR        => 'Core Error',
        E_CORE_WARNING      => 'Core Warning',
        E_COMPILE_ERROR     => 'Compile Error',
        E_COMPILE_WARNING   => 'Compile Warning',
        E_USER_ERROR        => 'User Error',
        E_USER_WARNING      => 'User Warning',
        E_USER_NOTICE       => 'User Notice',
        E_STRICT            => 'Strict',
        E_RECOVERABLE_ERROR => 'Recoverable Error',
        E_DEPRECATED        => 'Deprecated',
        E_USER_DEPRECATED   => 'User Deprecated',
    ];

    /** */
    protected $logger;

    /**
     * 构造函数
     * @param string|null $logPath 自定义日志路径，不传则使用默认路径
     */
    public function __construct($logPath = null)
    {
        $this->logPath = $logPath ?? dirname(__DIR__) . self::DEFAULT_LOG_PATH;
        $this->logger = new Logger();
    }

    /**
     * 初始化错误处理
     */
    public function init()
    {
        // 设置自定义错误处理器
        set_error_handler([$this, 'handleError']);
        
        // 设置自定义异常处理器
        set_exception_handler([$this, 'handleException']);
        
        // 设置关闭时执行的函数
        register_shutdown_function([$this, 'handleShutdown']);
    }
    
    /**
     * 恢复默认错误处理
     */
    public function restore()
    {
        restore_error_handler();
        restore_exception_handler();
    }

    /**
     * 自定义错误处理器
     * 
     * @param int $errno 错误级别
     * @param string $errstr 错误信息
     * @param string $errfile 错误文件
     * @param int $errline 错误行号
     * @return bool
     */
    public function handleError($errno, $errstr, $errfile = '', $errline = 0)
    {
        // 如果错误被@抑制，则不处理
        if (error_reporting() === 0) {
            return false;
        }

        $errorLevel = $this->errorLevels[$errno] ?? 'Unknown Error';
        
        $message = sprintf(
            "%s: %s in %s on line %d",
            $errorLevel,
            $errstr,
            $errfile,
            $errline
        );

        // 记录错误日志
        $this->logError($errstr);

        $this->logger->error($message);

        // 根据错误级别决定是否终止执行
        if (in_array($errno, [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR])) {
            exit('Catch Error: ' . $errstr);
        }

        // 对于非致命错误，返回false让PHP继续执行默认处理
        return false;
    }

    /**
     * 自定义异常处理器
     * 
     * @param \Throwable $exception
     */
    public function handleException($exception)
    {
        $message = sprintf(
            "Uncaught Exception: %s in %s on line %d\nStack Trace:\n%s",
            $exception->getMessage(),
            $exception->getFile(),
            $exception->getLine(),
            $exception->getTraceAsString()
        );

        $this->logError($message);

        $this->logger->error($exception->getMessage());
        
        exit('Catch Exception: ' . $exception->getMessage());
    }

    /**
     * 关闭时执行的函数
     */
    public function handleShutdown()
    {
        // 防止重复处理
        if ($this->shutdownHandled) {
            return;
        }
        
        $error = error_get_last();
        if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
            $this->shutdownHandled = true;
            $this->handleError($error['type'], $error['message'], $error['file'], $error['line']);
        }
    }

    /**
     * 记录错误日志
     * 
     * @param string $message
     */
    protected function logError($message)
    {
        // 确保日志目录存在
        $logDir = dirname($this->logPath);
        if (!is_dir($logDir)) {
            if (!mkdir($logDir, 0755, true)) {
                // 如果无法创建目录，尝试写入系统临时目录
                $this->logPath = zyucmini_get_temp_dir() . '/zyuc_miniv1_error.log';
            }
        }
        
        // 格式化日志消息
        $logMessage = sprintf(
            "[%s] %s\n",
            date('Y-m-d H:i:s'),
            $message
        );
        
        // 写入日志文件，检查是否成功
        $result = file_put_contents($this->logPath, $logMessage, FILE_APPEND | LOCK_EX);

        // 如果写入失败，尝试写入系统临时目录
        if ($result === false && $this->logPath !== zyucmini_get_temp_dir() . '/zyuc_miniv1_error.log') {
            $fallbackPath = zyucmini_get_temp_dir() . '/zyuc_miniv1_error.log';
            file_put_contents($fallbackPath, $logMessage, FILE_APPEND | LOCK_EX);
        }
    }

    /**
     * 触发自定义错误
     * 
     * @param string $message 错误信息
     * @param int $level 错误级别，默认为E_USER_ERROR
     */
    public function trigger($message, $level = E_USER_ERROR)
    {
        trigger_error($message, $level);
    }

    /**
     * 设置日志文件路径
     * 
     * @param string $logPath
     * @return string 当前日志路径
     */
    public function setLogPath($logPath)
    {
        $this->logPath = $logPath;
        return $this->logPath;
    }
    
    /**
     * 获取当前日志路径
     * 
     * @return string
     */
    public function getLogPath()
    {
        return $this->logPath;
    }
}